package aceim.protocol.snuk182.icq;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import aceim.api.IProtocol;
import aceim.api.dataentity.Buddy;
import aceim.api.dataentity.BuddyGroup;
import aceim.api.dataentity.ConnectionState;
import aceim.api.dataentity.FileMessage;
import aceim.api.dataentity.FileProgress;
import aceim.api.dataentity.ItemAction;
import aceim.api.dataentity.Message;
import aceim.api.dataentity.OnlineInfo;
import aceim.api.dataentity.ServiceMessage;
import aceim.api.dataentity.TextMessage;
import aceim.api.dataentity.tkv.TKV;
import aceim.api.service.AccountService;
import aceim.api.service.ApiConstants;
import aceim.api.service.ICoreProtocolCallback;
import aceim.api.service.ProtocolException;
import aceim.api.service.ProtocolException.Cause;
import aceim.api.utils.Logger;
import aceim.api.utils.Logger.LoggerLevel;
import aceim.api.utils.Utils;
import aceim.protocol.snuk182.icq.inner.ICQConstants;
import aceim.protocol.snuk182.icq.inner.ICQException;
import aceim.protocol.snuk182.icq.inner.ICQServiceInternal;
import aceim.protocol.snuk182.icq.inner.ICQServiceResponse;
import aceim.protocol.snuk182.icq.inner.dataentity.ICBMMessage;
import aceim.protocol.snuk182.icq.inner.dataentity.ICQBuddy;
import aceim.protocol.snuk182.icq.inner.dataentity.ICQBuddyGroup;
import aceim.protocol.snuk182.icq.inner.dataentity.ICQOnlineInfo;
import aceim.protocol.snuk182.icq.inner.dataentity.ICQPersonalInfo;
import aceim.protocol.snuk182.icq.utils.Base64;
import aceim.protocol.snuk182.icq.utils.ProtocolUtils;
import aceim.protocol.snuk182.icq.utils.ResourceUtils;
import android.content.Context;
import android.os.Parcelable;
import android.text.TextUtils;
public class ICQService extends AccountService {
public ICQService(byte serviceId, String protocolUid, ICoreProtocolCallback callback, Context context) {
super(serviceId, protocolUid, callback, context);
}
@Override
public IProtocol getProtocol() {
return protocol;
}
@Override
protected void timeoutReconnect() {
internal.getRunnableService().disconnect();
try {
internal.connectInternal(true);
} catch (ICQException e) {
Logger.log(e);
}
}
@Override
protected ConnectionState getCurrentState() {
return ICQEntityAdapter.icqConnectionState2ConnectionState(internal.getCurrentState());
}
@Override
protected void keepaliveRequest() {
try {
internal.request(ICQServiceInternal.REQ_KEEPALIVE_CHECK);
} catch (ICQException e) {
Logger.log(e);
}
}
private final IProtocol protocol = new IProtocol() {
@Override
public void requestFullInfo(String uid, boolean shortInfo) {
try {
if (!uid.equals(getProtocolUid())) {
if (shortInfo) {
internal.request(ICQServiceInternal.REQ_GETSHORTBUDDYINFO, uid);
} else {
internal.request(ICQServiceInternal.REQ_GETFULLBUDDYINFO, uid);
}
}
} catch (Exception e) {
Logger.log(e);
}
}
@Override
public void buddyAction(ItemAction action, Buddy buddy) {
ICQBuddy icqBuddy = ICQEntityAdapter.buddy2ICQBuddy(buddy);
try {
switch (action) {
case ADDED:
internal.request(ICQServiceInternal.REQ_ADDBUDDY, icqBuddy);
break;
case MODIFIED:
internal.request(ICQServiceInternal.REQ_EDITBUDDY, icqBuddy);
break;
case DELETED:
internal.request(ICQServiceInternal.REQ_REMOVEBUDDY, icqBuddy);
break;
default:
break;
}
} catch (Exception e) {
Logger.log(e);
}
}
@Override
public void buddyGroupAction(ItemAction action, BuddyGroup group) {
try {
ICQBuddyGroup icqBuddyGroup = ICQEntityAdapter.buddyGroup2ICQBuddyGroup(group);
switch (action) {
case ADDED:
internal.request(ICQServiceInternal.REQ_ADDGROUP, icqBuddyGroup);
break;
case DELETED:
internal.request(ICQServiceInternal.REQ_REMOVEGROUP, icqBuddyGroup);
break;
case MODIFIED:
internal.request(ICQServiceInternal.REQ_RENAMEGROUP, icqBuddyGroup);
break;
default:
break;
}
} catch (Exception e) {
Logger.log(e);
}
}
@Override
public void disconnect() {
try {
internal.request(ICQServiceInternal.REQ_DISCONNECT);
} catch (ICQException e) {
Logger.log(e);
}
}
@Override
public void connect(OnlineInfo info) {
try {
connectInternal(info);
} catch (ProtocolException e) {
Logger.log(e);
getCoreService().notification(e.getLocalizedMessage());
}
}
@Override
public long sendMessage(Message message) {
try {
Object result;
if (message instanceof TextMessage) {
result = internal.request(ICQServiceInternal.REQ_SENDMESSAGE, ICQEntityAdapter.textMessage2ICBMMessage((TextMessage) message));
} else if (message instanceof FileMessage) {
result = internal.request(ICQServiceInternal.REQ_SENDFILE, ICQEntityAdapter.fileMessage2IcbmMessage((FileMessage) message, getProtocolUid()));
} else {
result = null;
}
if (result instanceof Long) {
return (Long) result;
} else {
return 0;
}
} catch (ICQException e) {
Logger.log(e);
return 0;
}
}
@Override
public void requestIcon(String ownerUid) {
try {
internal.request(ICQServiceInternal.REQ_GETICON, ownerUid);
} catch (ICQException e) {
Logger.log(e);
}
}
@Override
public void messageResponse(Message message, boolean accept) {
try {
if (message instanceof FileMessage) {
internal.request(ICQServiceInternal.REQ_FILERESPOND, message.getMessageId(), accept);
} else if (message instanceof ServiceMessage) {
if (message.getContactDetail().equals(getContext().getString(R.string.ask_authorization))) {
internal.request(ICQServiceInternal.REQ_AUTHRESPONSE, message.getContactUid(), accept);
}
}
} catch (Exception e) {
Logger.log(e);
}
}
@Override
public void cancelFileFransfer(long messageId) {
try {
internal.request(ICQServiceInternal.REQ_FILECANCEL, messageId);
} catch (ICQException e) {
Logger.log(e);
}
}
@Override
public void sendTypingNotification(String ownerUid) {
try {
internal.request(ICQServiceInternal.REQ_SENDTYPING, ownerUid);
} catch (ICQException e) {
Logger.log(e);
}
}
@Override
public void uploadAccountPhoto(String filePath) {
try {
byte[] scaledImage = Utils.scaleAccountIcon(filePath, 60);
internal.request(ICQServiceInternal.REQ_UPLOADICON, scaledImage);
} catch (Exception e) {
Logger.log(e);
getCoreService().notification(e.getLocalizedMessage());
}
}
@Override
public void removeAccountPhoto() {
try {
internal.request(ICQServiceInternal.REQ_UPLOADICON, new byte[0]);
} catch (ICQException e) {
Logger.log(e);
}
}
@Override
public void setFeature(String featureId, OnlineInfo info) {
// This helps parsing Bundles
info.getFeatures().setClassLoader(TKV.class.getClassLoader());
try {
if (featureId.equals(ApiConstants.FEATURE_STATUS)) {
byte status = info.getFeatures().getByte(featureId);
int istatus = ICQEntityAdapter.userStatus2ICQUserStatus(status);
if (istatus < 0) {
byte[] qipStatus = ICQEntityAdapter.userQipStatus2ICQQipStatus(status);
if (qipStatus != null) {
internal.request(ICQServiceInternal.REQ_SETSTATUS, ICQConstants.STATUS_ONLINE, qipStatus);
} else {
Logger.log("Wrong status - " + status);
}
} else {
internal.request(ICQServiceInternal.REQ_SETSTATUS, istatus);
}
} else if (featureId.equals(ApiConstants.FEATURE_XSTATUS)) {
internal.request(ICQServiceInternal.REQ_SETEXTENDEDSTATUS, ICQEntityAdapter.onlineInfo2ICQOnlineInfo(info, info.getProtocolUid()));
} else if (featureId.equals(IcqApiConstants.FEATURE_ACCOUNT_VISIBILITY)) {
internal.request(ICQServiceInternal.REQ_ACCOUNTVISIBILITY, ICQEntityAdapter.ACCOUNT_VISIBILITY_MAPPING[info.getFeatures().getByte(featureId, (byte) 0)]);
} else if (featureId.equals(IcqApiConstants.FEATURE_BUDDY_VISIBILITY)) {
byte value = info.getFeatures().getByte(featureId, (byte) 0);
internal.request(ICQServiceInternal.REQ_BUDDYVISIBILITY, info.getProtocolUid(), value < 0 ? ICQConstants.VIS_REGULAR : ICQEntityAdapter.BUDDY_VISIBILITY_MAPPING[value]);
} else if (featureId.equals(IcqApiConstants.FEATURE_BUDDY_SEARCH)) {
Parcelable[] p = info.getFeatures().getParcelableArray(featureId);
if (p == null || p.length < 1) {
Logger.log("No Parcelable at ICQ Buddy search request", LoggerLevel.INFO);
return;
}
Map<String, String> map = ICQEntityAdapter.searchTKVListToMap(p, getContext());
internal.request(ICQServiceInternal.REQ_SEARCHFORBUDDY, map);
} else if (featureId.equals(IcqApiConstants.FEATURE_AUTHORIZATION)) {
Parcelable[] p = info.getFeatures().getParcelableArray(featureId);
if (p == null || p.length < 1) {
Logger.log("No Parcelable at ICQ Auth request", LoggerLevel.INFO);
return;
}
String reasonKey = getContext().getString(R.string.message);
for (Parcelable pp : p) {
TKV tkv = (TKV) pp;
if (tkv.getKey().equals(reasonKey)) {
internal.request(ICQServiceInternal.REQ_AUTHREQUEST, info.getProtocolUid(), tkv.getValue());
return;
}
}
Logger.log("No reason message at ICQ Auth request", LoggerLevel.INFO);
}
} catch (ICQException e) {
Logger.log(e);
}
}
@Override
public void joinChatRoom(String chatId, boolean loadOccupantsIcons) {
}
@Override
public void leaveChatRoom(String chatId) {
}
};
private final ICQServiceResponse icqResponse = new ICQServiceResponse() {
@SuppressWarnings("unchecked")
@Override
public Object respond(short action, Object... args) {
try {
switch (action) {
case ICQServiceResponse.RES_LOG:
Logger.log((String) args[0]);
break;
case ICQServiceResponse.RES_CONNECTED:
getCoreService().connectionStateChanged(ConnectionState.CONNECTED, 0);
sendKeepalive();
break;
case ICQServiceResponse.RES_DISCONNECTED:
closeKeepaliveThread();
int errorCode = -1;
if (args.length > 0 && !TextUtils.isEmpty((CharSequence) args[0])) {
String error = args[0].toString();
getCoreService().notification(error);
if (ICQServiceInternal.ERROR_WRONG_PASSWORD.equals(error)) {
errorCode = ProtocolException.Cause.CANNOT_AUTHORIZE.ordinal();
} else if (ICQServiceInternal.ERROR_RATE_LIMIT_EXCEEDED.equals(error)) {
errorCode = ProtocolException.Cause.CONNECTION_LIMIT_EXCEEDED.ordinal();
}
}
getCoreService().connectionStateChanged(ConnectionState.DISCONNECTED, errorCode);
break;
case ICQServiceResponse.RES_SAVEIMAGEFILE:
getCoreService().iconBitmap((String) args[1], (byte[]) args[0], Base64.encodeBytes((byte[]) args[2]));
break;
case ICQServiceResponse.RES_CLUPDATED:
List<Buddy> buddyList = ICQEntityAdapter.ICQBuddyList2Buddylist((List<ICQBuddy>) args[0], getProtocolUid(), getServiceId());
List<BuddyGroup> buddyGroupList = ICQEntityAdapter.ICQBuddyGroupList2BuddyGroupList((List<ICQBuddyGroup>) args[1], getProtocolUid(), getServiceId(), (List<ICQBuddy>) args[0], buddyList);
getCoreService().buddyListUpdated(buddyGroupList);
break;
case ICQServiceResponse.RES_KEEPALIVE:
resetHeartbeat();
break;
case ICQServiceResponse.RES_MESSAGE:
ICBMMessage msg = (ICBMMessage) args[0];
if (msg.senderId.equals(getProtocolUid())) {
resetHeartbeat();
return null;
}
TextMessage txtmessage = ICQEntityAdapter.icbmMessage2TextMessage(msg, getServiceId());
getCoreService().message(txtmessage);
break;
case ICQServiceResponse.RES_BUDDYSTATECHANGED:
OnlineInfo buddiOnlineInfo = ICQEntityAdapter.icqOnlineInfo2OnlineInfo((ICQOnlineInfo) args[0], getProtocolUid(), getServiceId());
getCoreService().buddyStateChanged(Arrays.asList(buddiOnlineInfo));
if (buddiOnlineInfo.getFeatures().getByte(ApiConstants.FEATURE_XSTATUS, (byte) -1) > -1 && args.length < 2) {
internal.getMessagingEngine().askForXStatus(buddiOnlineInfo.getProtocolUid());
}
break;
case ICQServiceResponse.RES_CONNECTING:
getCoreService().connectionStateChanged(ConnectionState.CONNECTING, (Integer) args[0]);
break;
case ICQServiceResponse.RES_FILEMESSAGE:
getCoreService().message(ICQEntityAdapter.icbmMessage2FileMessage((ICBMMessage) args[0], getServiceId()));
break;
case ICQServiceResponse.RES_NOTIFICATION:
getCoreService().notification((String) args[0]);
break;
case ICQServiceResponse.RES_ACCOUNTUPDATED:
getCoreService().accountStateChanged(ICQEntityAdapter.icqOnlineInfo2OnlineInfo((ICQOnlineInfo) args[0], getProtocolUid(), getServiceId()));
break;
case ICQServiceResponse.RES_USERINFO:
getCoreService().personalInfo(ICQEntityAdapter.icqPersonalInfo2PersonalInfo((ICQPersonalInfo) args[0], getContext(), getServiceId()), !((Boolean)args[1]));
break;
case ICQServiceResponse.RES_AUTHREQUEST:
getCoreService().message(ICQEntityAdapter.authRequestToServiceMessage(getServiceId(), (String) args[0], (String) args[1], getContext()));
break;
case ICQServiceResponse.RES_SEARCHRESULT:
getCoreService().searchResult(ICQEntityAdapter.icqPersonalInfos2PersonalInfos((List<ICQPersonalInfo>) args[0], getContext(), getServiceId()));
break;
case ICQServiceResponse.RES_GROUPADDED:
getCoreService().groupAction(ItemAction.ADDED, ICQEntityAdapter.ICQBuddyGroup2BuddyGroup((ICQBuddyGroup) args[0], internal.getUn(), getServiceId(), internal.getBuddyList().buddyList, null));
break;
case ICQServiceResponse.RES_BUDDYADDED:
getCoreService().buddyAction(ItemAction.ADDED, ICQEntityAdapter.ICQBuddy2Buddy((ICQBuddy) args[0], internal.getUn(), getServiceId()));
break;
case ICQServiceResponse.RES_BUDDYDELETED:
getCoreService().buddyAction(ItemAction.DELETED, ICQEntityAdapter.ICQBuddy2Buddy((ICQBuddy) args[0], internal.getUn(), getServiceId()));
break;
case ICQServiceResponse.RES_GROUPDELETED:
getCoreService().groupAction(ItemAction.DELETED, ICQEntityAdapter.ICQBuddyGroup2BuddyGroup((ICQBuddyGroup) args[0], internal.getUn(), getServiceId(), internal.getBuddyList().buddyList, null));
break;
case ICQServiceResponse.RES_BUDDYMODIFIED:
getCoreService().buddyAction(ItemAction.MODIFIED, ICQEntityAdapter.ICQBuddy2Buddy((ICQBuddy) args[0], internal.getUn(), getServiceId()));
break;
case ICQServiceResponse.RES_GROUPMODIFIED:
getCoreService().groupAction(ItemAction.MODIFIED, ICQEntityAdapter.ICQBuddyGroup2BuddyGroup((ICQBuddyGroup) args[0], internal.getUn(), getServiceId(), internal.getBuddyList().buddyList, null));
break;
case ICQServiceResponse.RES_FILEPROGRESS:
FileProgress fp = new FileProgress(getServiceId(), ProtocolUtils.bytes2LongBE((byte[]) args[0], 0), (String) args[1], (Long) args[2], (Long) args[3], (Boolean) args[4], (String) args[6], (String) args[5]);
getCoreService().fileProgress(fp);
break;
case ICQServiceResponse.RES_MESSAGEACK:
getCoreService().messageAck((String) args[0], (Long) args[1], ICQEntityAdapter.icqMessageAck2MessageAck((Byte) args[2]));
break;
case ICQServiceResponse.RES_TYPING:
getCoreService().typingNotification((String) args[0]);
break;
case ICQServiceResponse.RES_ACCOUNT_ACTIVITY:
getCoreService().accountActivity((String) args[0]);
break;
case ICQServiceResponse.RES_GET_FILE_FOR_SAVING:
return Utils.createLocalFileForReceiving((String) args[0], ICQEntityAdapter.ICQBuddy2Buddy((ICQBuddy) args[1], getProtocolUid(), (byte) 0), (Long) args[2]);
}
} catch (Exception e) {
Logger.log(e);
}
return null;
}
};
private void connectInternal(OnlineInfo info) throws ProtocolException {
try {
String username = getCoreService().requestPreference(ResourceUtils.KEY_USERNAME);
String password = getCoreService().requestPreference(ResourceUtils.KEY_PASSWORD);
String host = getCoreService().requestPreference(ResourceUtils.KEY_LOGIN_HOST);
String port = getCoreService().requestPreference(ResourceUtils.KEY_LOGIN_PORT);
String ping = getCoreService().requestPreference(ResourceUtils.KEY_PING_TIMEOUT);
String secureLogin = getCoreService().requestPreference(ResourceUtils.KEY_SECURE_LOGIN);
if (ping != null){
try {
pingTimeout = Integer.parseInt(ping);
} catch (Exception e) {}
}
if (username == null || password == null){
throw new ProtocolException(Cause.BROKEN_AUTH_DATA);
}
internal.request(ICQServiceInternal.REQ_CONNECT, username, password, host, port, ICQEntityAdapter.onlineInfo2ICQOnlineInfo(info, info.getProtocolUid()), Boolean.parseBoolean(secureLogin));
} catch (Exception e) {
throw new ProtocolException(e.getMessage());
}
}
private ICQServiceInternal internal = new ICQServiceInternal(icqResponse);
}